#pragma once
#include <vector>
#include <stdexcept>

template<typename T>
class Matrix {
private:
    std::vector<T> data;

public:
    int rows;
    int cols;

    Matrix() : rows(0), cols(0) {}

    Matrix(int rows, int cols) : rows(rows), cols(cols) {
        data.resize(rows * cols);
    }

    Matrix(int rows, int cols, const T& init_value) : rows(rows), cols(cols) {
        data.resize(rows * cols, init_value);
    }

    T& operator()(int r, int c) {
        if (r < 0 || r >= rows || c < 0 || c >= cols) {
            throw std::out_of_range("Matrix index out of range");
        }
        return data[r * cols + c];
    }

    const T& operator()(int r, int c) const {
        if (r < 0 || r >= rows || c < 0 || c >= cols) {
            throw std::out_of_range("Matrix index out of range");
        }
        return data[r * cols + c];
    }

    void append_row(const std::vector<T>& row) {
        if (cols == 0) {
            cols = row.size();
        }
        else if (row.size() != cols) {
            throw std::invalid_argument("Row size doesn't match matrix columns");
        }

        data.insert(data.end(), row.begin(), row.end());
        rows++;
    }

    void resize(int new_rows, int new_cols) {
        rows = new_rows;
        cols = new_cols;
        data.resize(rows * cols);
    }

    void clear() {
        rows = 0;
        cols = 0;
        data.clear();
    }

    const std::vector<T>& get_data() const { return data; }
    std::vector<T>& get_data() { return data; }

    bool empty() const { return rows == 0 || cols == 0; }
};